/*
 * chat_server.c
 *
 *  Created on: Apr 9, 2012
 *      Author: root
 */
/* include fig01 */
#include	"unp.h"
//#define FD_SETSIZE 10
#define USER "230 User logged in\r\n\r\n"
#define TO   "200 OK\r\n\r\n"
#define QUIT "221 service closing\r\n\r\n"
#define OK   "200 OK\r\n\r\n"
#define NOT_LOG "530 Not logged in\r\n\r\n"
#define SYN_ERR "500 Syntax error, command unrecognized\r\n\r\n"

typedef struct {
	int read_cnt;
	char *read_ptr;
	char *read_buf;
} ReadBuffer;

typedef struct {
	int connfd;
	char *line;
	ReadBuffer readBuffer;
} Client;

typedef struct {
	char users[10];
	char messages[MAXLINE];
	char servbuf[MAXLINE];
	char talkbuf[MAXLINE];
} Info;

typedef struct From {
	char frommsg[MAXLINE];
} From;

typedef struct Divide {
	char divide[MAXLINE];
} Divide;

ReadBuffer *p;
Client user[FD_SETSIZE];
Info info[FD_SETSIZE];
From from[5];
Divide divide[5];
ssize_t my_read(int fd, char *ptr, ReadBuffer readBuffer);
ssize_t readcrlf_r(int fd, void *vptr, size_t maxlen, ReadBuffer readBuffer);

int main(int argc, char **argv) {
	int i, maxi, maxfd, listenfd, connfd, sockfd;
	int msg = 0, rq = 0;
	int err = 0;
	int flag = 0;
	int flag1 = 0;
	int flag2 = 0;
	int count;
	int a;
	int nready;
	int log[FD_SETSIZE];

	char *command;
	char *command1;
	ssize_t n;
	fd_set rset, allset;
	char buf[MAXLINE];
	socklen_t clilen;
	struct sockaddr_in cliaddr, servaddr;

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);

	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

	Listen(listenfd, LISTENQ);

	maxfd = listenfd; /* initialize */
	maxi = -1; /* index into client[] array */
	for (i = 0; i < FD_SETSIZE; i++) {
		user[i].connfd = -1; /* -1 indicates available entry */
		log[i] = -1;
	}
	FD_ZERO(&allset);
	FD_SET(listenfd, &allset);
	/* end fig01 */

	/* include fig02 */
	for (;;) {
		rset = allset; /* structure assignment */
		nready = Select(maxfd + 1, &rset, NULL, NULL, NULL);

		if (FD_ISSET(listenfd, &rset)) { /* new client connection */
			clilen = sizeof(cliaddr);
			connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

#ifdef	NOTDEF
			printf("new client: %s, port %d\n",
					Inet_ntop(AF_INET, &cliaddr.sin_addr, 4, NULL),
					ntohs(cliaddr.sin_port));
#endif

			for (i = 0; i < FD_SETSIZE; i++)
				if (user[i].connfd < 0) {
					user[i].connfd = connfd; /* save descriptor */
					printf("save client[%d] : %d\n", i, user[i].connfd);
					user[i].line = Malloc(MAXLINE);
					p = &user[i].readBuffer;
					p->read_cnt = 0;
					p->read_buf = Malloc(MAXLINE);
					count++;
					break;
				}
			if (i == FD_SETSIZE)
				err_quit("too many clients");

			FD_SET(connfd, &allset);
			/* add new descriptor to set */
			if (connfd > maxfd)
				maxfd = connfd; /* for select */
			if (i > maxi)
				maxi = i; /* max index in client[] array */

			if (--nready <= 0)
				continue; /* no more readable descriptors */
		}

		for (i = 0; i <= maxi; i++) { /* check all clients for data */
			if ((sockfd = user[i].connfd) < 0)
				continue;
			if (FD_ISSET(user[i].connfd, &rset)) {

				if ((n = readcrlf_r(user[i].connfd, user[i].line, MAXLINE, *p))
						== 0) {
					/*4connection closed by client */
					Close(user[i].connfd);
					FD_CLR(user[i].connfd, &allset);
					log[user[i].connfd] = -1; // clear logged int
					user[i].connfd = -1;
					printf("close client[%d]\n", i);
					printf("%s log out!!!!\n", info[user[n].connfd].users);
					free(user[i].line);
					free(user[i].readBuffer.read_buf);
					count--;
				} else

				if (!strcmp(user[i].line, "")) {
					continue;
				}

				if (!strcmp(user[i].line, "error")) {

					memset(user[i].line, 0, strlen(user[i].line));
				}

				if (msg == 1) { // FROM request message parsing 2

					sprintf(info[user[i].connfd].messages, "%s", user[i].line);
					Fputs(info[user[i].connfd].messages, stdout);

					sprintf(info[i].servbuf, "FROM %s TO %s\r\n%s\r\n\r\n",
							from[1].frommsg, from[3].frommsg,
							info[user[i].connfd].messages);
					printf("\n---------------\n");
					Fputs(info[i].servbuf, stdout);
					printf("----------------\n");

					memset(user[i].line, 0, strlen(user[i].line));
					crlf_strip(user[i].line, MAXLINE, info[i].servbuf,
							strlen(info[i].servbuf));

					//Fputs(user[i].line, stdout);
					rq = 0;
					command = strtok(user[i].line, "\n");
					while (command != NULL) {
						sprintf(from[rq].frommsg, "%s", command);
						command = strtok(NULL, "\r\n");
						rq++;
					}
					rq = 0;
					command1 = strtok(from[0].frommsg, " ");
					while (command1 != NULL) {
						sprintf(divide[rq].divide, "%s", command1);
						command1 = strtok(NULL, " ");
						rq++;
					}

					///////////////////all message PART///////////////////
					if (!strcmp(divide[3].divide, "all")) {

						printf("all message from %s\n", divide[1].divide);
						printf("----------------\n");

						for (a = 0; a < count; a++) {
							///////////other user not login//////////
							if (count == 1) //1 log in , but all message
									{

								Writen(user[i].connfd, NOT_LOG,
										strlen(NOT_LOG));

								printf("NOT login user\n");
								printf("---------------\n");
								break;

							}

							//////////////other user connect, not loggin//////////
							if (log[user[a].connfd] != user[a].connfd) {

								if (flag1 == 0) {
									printf("---------------\n");
									Writen(user[i].connfd, NOT_LOG,
											strlen(NOT_LOG));
									printf("NOT login user\n");
									flag1 = 1;
									printf("---------------\n");
								}

								continue;
							}

							else {

								if (user[a].connfd != sockfd)

								{
									sprintf(info[i].talkbuf,
											"299 Message delivered\r\nFROM %s TO %s\r\n%s\r\n\r\n",
											divide[1].divide,
											info[user[a].connfd].users,
											from[1].frommsg);

									Writen(user[a].connfd, info[i].talkbuf,
											strlen(info[i].talkbuf)); // response message

									if (flag == 0) {
										Writen(user[i].connfd, OK, strlen(OK)); //200 OK request message
										flag = 1;
									}
									err = 1;
								}
							}
						}
						flag = 0;
						flag1 = 0;

						for (rq = 0; rq < 5; rq++) {
							memset(divide[rq].divide, 0,
									strlen(divide[rq].divide));
						}
					}

					///////////////////////1:1 message PART////////////////////////////////
					if (!strcmp(info[user[i].connfd].users, divide[1].divide)) {
						printf("from %s to %s message\n", divide[1].divide,
								divide[3].divide);
						printf("-------------\n");

						for (a = 0; a < count; a++) {

							///////////other user not login//////////
							if (count == 1) {
								{

									Writen(user[i].connfd, NOT_LOG,
											strlen(NOT_LOG));
									printf("NOT login user\n");
									printf("---------------\n");
									break;

								}
							}

							////////////confirm NOT LOGGIN//////////////////
							if (log[user[a].connfd] != user[a].connfd) // confirm not log in user
									{

								if (flag2 == 0) {
									printf("---------------\n");
									Writen(user[i].connfd, NOT_LOG,
											strlen(NOT_LOG));
									printf("NOT login user\n");
									printf("---------------\n");
									flag2 = 1;
								}
								continue;

							}
							/////////////////////////////////
							if (!strcmp(divide[3].divide,
									info[user[a].connfd].users)) {

								sprintf(info[i].talkbuf,
										"299 Message delivered\r\nFROM %s TO %s\r\n%s\r\n\r\n",
										divide[1].divide,
										info[user[a].connfd].users,
										from[1].frommsg);

								Writen(user[a].connfd, info[i].talkbuf,
										strlen(info[i].talkbuf)); //299 response message

								if (flag == 0) {
									Writen(user[i].connfd, OK, strlen(OK)); //200 OK request message
									flag = 1;
								}

							}
						}
						flag2 = 0;
						flag = 0;
						for (rq = 0; rq < 5; rq++) {
							memset(divide[rq].divide, 0,
									strlen(divide[rq].divide));

						}
					}
					memset(user[i].line, 0, strlen(user[i].line));
					memset(info[i].messages, 0, strlen(info[i].messages));
					memset(info[i].servbuf, 0, strlen(info[i].servbuf));
					memset(info[i].talkbuf, 0, strlen(info[i].talkbuf));
					rq = 0;
					msg = 0;
					err = 1;
				}
				///////////////////////////////////////////////////////

				sprintf(info[i].servbuf, "%s", user[i].line);
				command = strtok(info[i].servbuf, " ");

				///////////////////////////QUIT PART///////////////////////////////
				if (!strcmp("QUIT", info[i].servbuf)) {
					printf("*****************close client***************\n");
					memset(info[user[i].connfd].users, 0,
							strlen(info[user[i].connfd].users));
					printf("size %d\n", strlen(info[user[i].connfd].users));
					Writen(user[i].connfd, QUIT, strlen(QUIT));
					err = 1;
					Close(user[i].connfd);
					FD_CLR(user[i].connfd, &allset);
					log[user[i].connfd] = -1; // clear logged int
					user[i].connfd = -1;
					printf("close client[%d]\n", i);
					printf("%s log out!!!!\n", info[user[n].connfd].users);
					free(user[i].line);
					free(user[i].readBuffer.read_buf);
					count--;

				}
				////////////////////////////////////////////////////////////

				///////////////////////////////USER PART/////////////////////////
				if (!strcmp(info[i].servbuf, "USER")) {

					while (command != NULL) {

						sprintf(info[user[i].connfd].users, "%s", command);
						command = strtok(NULL, " ,\r\n\r\n");
					}
					printf("%s log in!!!!\n", info[user[i].connfd].users);
					Writen(user[i].connfd, USER, strlen(USER)); // request message

					for (a = 0; a < FD_SETSIZE; a++) { // for confirm log in
						if (user[a].connfd == sockfd) {
							log[sockfd] = user[a].connfd;
						}
					}

					memset(user[i].line, 0, strlen(user[i].line));
					memset(info[i].servbuf, 0, strlen(info[i].servbuf));

					err = 1;
				}
				////////////////////////////////////////////////////////////////

				/////////////////////////WHO PART////////////////////////////////

				if (!strcmp(info[i].servbuf, "WHO")) { //WHO PART

					sprintf(info[i].servbuf, "210 User List\r\n");

					for (n = 0; n < FD_SETSIZE; n++) {
						if (user[n].connfd > 0) { //distinct log in ID

							strcat(info[i].servbuf, info[user[n].connfd].users);
							strcat(info[i].servbuf, " ");

							strcat(user[i].line, info[user[n].connfd].users);
							strcat(user[i].line, " ");
						}
					}
					strcat(info[i].servbuf, "\r\n\r\n");
					printf("who message\n");
					Writen(user[i].connfd, info[i].servbuf,
							strlen(info[i].servbuf)); // response message

					memset(info[i].servbuf, 0, strlen(info[i].servbuf));
					memset(user[i].line, 0, strlen(user[i].line));
					err = 1;
				}
				////////////////////////////////////////////////////////////////////

				///////////////////////////////FROM////////////////////////////////
				if (!strcmp(info[i].servbuf, "FROM")) { //FROM request message parsing 1

					//Fputs(user[i].line, stdout);
					command = strtok(user[i].line, " ");
					while (command != NULL) {

						sprintf(from[rq].frommsg, "%s", command);
						command = strtok(NULL, " \r");
						rq++;

					}

					msg = 1;

					for (rq = 0; rq < 5; rq++) {
						printf("%s\n", from[rq].frommsg);
					}

					if (!strcmp(from[3].frommsg, "")) {

						msg = 0;
					}

					if (!strcmp(from[1].frommsg, "TO")) {

						msg = 0;
					}

					rq = 0;
					err = 1;

				}
				////////////////////////////////////////////////////////

				///////////////////syntex error////////////////////////
				if (err == 0)
					if (user[i].connfd > 0) // for bad file discriptor
							{
						Fputs(SYN_ERR, stdout);
						Writen(user[i].connfd, SYN_ERR, strlen(SYN_ERR));
					}
				memset(buf, 0, strlen(buf));
				memset(user[i].line, 0, strlen(user[i].line));
				err = 0;
				///////////////////////////////////////////////////////
				if (--nready <= 0)

					break; /* no more readable descriptors */

			}
		}
	}
}

/* end fig02 */
ssize_t my_read(int fd, char *ptr, ReadBuffer readBuffer) {

	if (p->read_cnt <= 0) {
		again: if ((p->read_cnt = read(fd, p->read_buf, sizeof(p->read_buf)))
				< 0) {
			if (errno == EINTR)
				goto again;
			return (-1);
		} else if (p->read_cnt == 0)
			return (0);
		p->read_ptr = p->read_buf;
	}

	p->read_cnt--;
	*ptr = *(p->read_ptr)++;
	return (1);
}

ssize_t readcrlf_r(int fd, void *vptr, size_t maxlen, ReadBuffer readBuffer) {
	ssize_t n, rc;
	char c, *ptr;
	char lastc = 0;

	ptr = vptr;
	for (n = 1; n < maxlen; n++) {
		if ((rc = my_read(fd, &c, readBuffer)) == 1) {
			*ptr = c;
			if (c == '\n') {
				if (lastc == '\r') {
					ptr--;
				}
				break;
			}
			ptr++;
			lastc = c;

		} else if (rc == 0) {
			*ptr = 0;
			return (n - 1);
		} else
			return -1;
	}
	*ptr = 0;
	return (n);
}

